package com.nanbei.queue;

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @ClassDescription: 阻塞队列双锁实现
 * @JdkVersion: 17
 * @Author: BaoShan.li
 * @Created: 2023/12/4 9:47
 */
public class DoubleBlockingQueue<E> implements BlockingQueueInterface<E> {

    AtomicInteger size = new AtomicInteger(0);
    int head;
    int tail;
    E[] arrays;

    ReentrantLock headLock = new ReentrantLock();
    Condition headAwait = headLock.newCondition();
    ReentrantLock tailLock = new ReentrantLock();
    Condition tailAwait = tailLock.newCondition();

    @SuppressWarnings("all")
    public DoubleBlockingQueue(int capacity) {
        this.arrays = (E[]) new Object[capacity];
    }


    @Override
    public void offer(E e) throws InterruptedException {
        tailLock.lockInterruptibly();
        int temp;
        try {
            while (isFull()) {
                tailAwait.await();
            }

            arrays[tail] = e;
            if (++tail == arrays.length) {
                tail = 0;
            }
            temp = size.getAndIncrement();

            if (temp+1 < arrays.length){
                tailAwait.signal();
            }

        } finally {
            tailLock.unlock();
        }

        if (temp == 0) {
            headLock.lock();
            try {
                headAwait.signal();
            } finally {
                headLock.unlock();
            }
        }


    }

    @Override
    public void offer(E e, long timeout) throws InterruptedException {

    }

    @Override
    public E poll() throws InterruptedException {
        headLock.lockInterruptibly();
        E e;
        int temp;
        try {
            while (isEmpty()) {
                headAwait.await();
            }

            e = arrays[head];
            arrays[head] = null;

            if (++head == arrays.length) {
                head = 0;
            }

            temp = size.getAndDecrement();

            if (temp > 1) {
                headAwait.signal();
            }

        } finally {
            headLock.unlock();
        }

        if (temp == arrays.length) {
            tailLock.lock();
            try {
                tailAwait.signal();
            } finally {
                tailLock.unlock();
            }
        }


        return e;
    }

    @Override
    public boolean isEmpty() {
        return false;
    }

    @Override
    public boolean isFull() {
        return false;
    }
}
